/**
 * @Project Name:cola-code-generate
 * @Package Name:cn.hoofungson.framework.cola.code.generate.helper
 * @Since JDK 1.8
 */

package cn.com.scgeling.yzxl.code.generate.helper;

import cn.com.scgeling.yzxl.code.generate.bean.EntityFieldVO;
import cn.com.scgeling.yzxl.code.generate.util.FreemarkerUtils;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.db.ds.druid.DruidDSFactory;
import cn.hutool.json.JSONUtil;
import cn.hutool.setting.Setting;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Description: AutoCodeGenerateHelper
 * @Author 胡松 hoofungson@163.com
 * @Date 2022-06-16 11:59
 * @Version V1.0
 */
@Slf4j
public class AutoCodeGenerateHelper {

    public static void main(String[] args) throws SQLException {
        Setting setting = new Setting();
        setting.put("url", "jdbc:mysql://192.168.5.244:3306/swxl_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8");
        setting.put("username", "root");
        setting.put("password", "root");

        log.info("请输入您要生成的数据表名，多个表名之间用英文逗号分隔：");
        // 请输入数据库表名
        Scanner scanner = new Scanner(System.in);
        String inputText = scanner.nextLine();

        while (!StringUtils.hasText(inputText)) {
            log.info("内容不能为空，请输入您要生成的数据表名，多个表名之间用英文分隔：");
            scanner = new Scanner(System.in);
            inputText = scanner.nextLine();
        }

        log.info("打印用户输入内容：{}", inputText);
        String[] inputTextArray = inputText.split(",");
        StringBuilder inputStringBuilder = new StringBuilder();
        for(int i = 0; i < inputTextArray.length; i ++) {
            inputStringBuilder.append("'");
            inputStringBuilder.append(inputTextArray[i]);
            inputStringBuilder.append("'");
            if(i < inputTextArray.length - 1) {
                inputStringBuilder.append(",");
            }
        }
        // 开始生成数据表相关代码
        log.info("开始生成数据表相关业务代码：.....................");
        AutoCodeGenerateHelper autoCodeGenerateHelper = new AutoCodeGenerateHelper();
        autoCodeGenerateHelper.generateCode(setting, autoCodeGenerateHelper.getDbTableNames(setting, inputStringBuilder.toString()));
        log.info("相关数据表业务代码已生成=========================");
    }

    /**
     * 获取指定数据库所有数据表名
     *
     * @param setting
     * @return
     **/
    public List<Entity> getDbTableNames(Setting setting) throws SQLException {
        String queryTablesSQL = "SELECT table_name, table_comment, create_time, update_time FROM information_schema.TABLES WHERE table_schema= (SELECT DATABASE())";
        DSFactory.setCurrentDSFactory(new DruidDSFactory(setting));
        List<Entity> result = Db.use().query(queryTablesSQL);
        log.info("result: {}", JSONUtil.toJsonPrettyStr(result));
        return result;
    }

    /**
     * 获取指定数据库所有数据表名
     *
     * @param setting
     * @return
     **/
    public List<Entity> getDbTableNames(Setting setting, String inputText) throws SQLException {
        String queryTablesSQL = "SELECT table_name, table_comment, create_time, update_time FROM information_schema.TABLES WHERE table_schema= (SELECT DATABASE()) AND TABLE_NAME in (" + inputText + ")";
        DSFactory.setCurrentDSFactory(new DruidDSFactory(setting));
        List<Entity> result = Db.use().query(queryTablesSQL);
        log.info("result: {}", JSONUtil.toJsonPrettyStr(result));
        return result;
    }

    /**
     * 获取指定表字段
     *
     * @param setting
     * @param tableName
     * @return
     **/
    public List<Entity> getDbTableColumns(Setting setting, String tableName) throws SQLException {
        String queryTableColumnsSQL = "select column_name, data_type, column_comment from " +
                "information_schema.COLUMNS where table_schema = (SELECT DATABASE()) and table_name = '" + tableName + "';";
        DSFactory.setCurrentDSFactory(new DruidDSFactory(setting));
        List<Entity> result = Db.use().query(queryTableColumnsSQL);
        log.info("result: {}", JSONUtil.toJsonPrettyStr(result));
        return result;
    }

    /**
     * 生成业务代码
     *
     * @param setting
     * @param entityList
     **/
    public void generateCode(Setting setting, List<Entity> entityList) {
        entityList.forEach(entity -> {
            try {
                Map<String, Object> dataModelMap = parseTable(setting, entity);
                // 生成Model层代码
                generateModel(dataModelMap);
                generateDao(dataModelMap);
                generateConvert(dataModelMap);
                generateBusiness(dataModelMap);
                generateController(dataModelMap);
                generateClient(dataModelMap);
//                generateApp(dataModelMap);
//                generateAdapter(dataModelMap);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (TemplateException e) {
                throw new RuntimeException(e);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 解析数据表
     *
     * @param setting
     * @param table
     * @return
     **/
    public Map<String, Object> parseTable(Setting setting, Entity table) throws SQLException {
        String tableName = table.getStr("table_name");
        tableName = tableName.replace("tb_", "");
        String domainName = tableName.split("_")[0];
        String[] my = tableName.split("_");
        String className = "";
        // 小写类名
        String lowerCaseClassName = "";
        if (my.length <= 1) {
            className = my[0].substring(0, 1).toUpperCase() + my[0].substring(1);
            lowerCaseClassName = my[0];
        } else {
            for (int i = 0; i < my.length; i++) {
                className += my[i].substring(0, 1).toUpperCase() + my[i].substring(1);
                if (i == 0) {
                    lowerCaseClassName += my[i];
                } else {
                    lowerCaseClassName += my[i].substring(0, 1).toUpperCase() + my[i].substring(1);
                }
            }
        }
        List<EntityFieldVO> fields = new ArrayList<>();
        List<Entity> tableColumns = getDbTableColumns(setting, table.getStr("table_name"));
        tableColumns.forEach(entity -> {
            String columnName = entity.getStr("column_name");
            if ("id".equals(columnName) || "creator".equals(columnName) || "create_time".equals(columnName)) {

            } else {
                String[] columnNameArrays = entity.getStr("column_name").split("_");
                String fieldName = "";
                for (int i = 0; i < columnNameArrays.length; i++) {
                    if (i > 0) {
                        fieldName += columnNameArrays[i].substring(0, 1).toUpperCase() + columnNameArrays[i].substring(1);
                    } else {
                        fieldName += columnNameArrays[i];
                    }
                }
                String fieldType = entity.getStr("data_type");
                if (fieldType.equals("int")) fieldType = "Integer";
                if (fieldType.equals("bigint")) fieldType = "Long";
                if (fieldType.equals("decimal")) fieldType = "BigDecimal";
                if (fieldType.equals("varchar") || fieldType.equals("text") || fieldType.equals("longtext"))
                    fieldType = "String";
                if (fieldType.equals("datetime")) fieldType = "LocalDateTime";
                String fieldComment = entity.getStr("column_comment");
                fields.add(new EntityFieldVO(fieldName, fieldType, fieldComment));
            }
        });
        Map<String, Object> dataModelMap = new HashMap<>();
        dataModelMap.put("projectName", "swxl-cloud");
        dataModelMap.put("projectPackageName", "io.swxl");
        dataModelMap.put("moduleName", "model");
        dataModelMap.put("domainName", domainName);
        dataModelMap.put("datetime", LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
        dataModelMap.put("tableName", table.getStr("table_name"));
        dataModelMap.put("className", className);
        dataModelMap.put("lowerCaseClassName", lowerCaseClassName);
        dataModelMap.put("fields", fields);
        return dataModelMap;
    }

    /**
     * 生成domain模块代码
     *
     * @param dataModelMap
     **/
    public void generateModel(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/model");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\model\\" + dataModelMap.get("domainName");
        log.info("正在生成Domain模块......");
        // 生成实体
        log.info("正在生成{}...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "PO.ftl", dataModelMap, toPath, dataModelMap.get("className") + "PO.java");
    }

    /**
     * 生成infrastructure模块代码
     *
     * @param dataModelMap
     **/
    public void generateDao(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/dao");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\dao\\" + dataModelMap.get("domainName");
        log.info("正在生成Dao模块......");
        // 生成PO
//        log.info("正在生成{}PO...", dataModelMap.get("className"));
//        FreemarkerUtils.generateFile(ftlPath + "/", "DO.ftl", dataModelMap, toPath, dataModelMap.get("className") + "DO.java");
        // 生成Mapper
        log.info("正在生成{}Mapper...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath + "/mapper", "Mapper.ftl", dataModelMap, toPath, dataModelMap.get("className") + "Mapper.java");
        // 生成DaoService
        log.info("正在生成{}DaoService...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath + "/service/interfaces", "DaoService.ftl", dataModelMap, toPath, dataModelMap.get("className") + "DaoService.java");
        // 生成DaoServiceImpl
        log.info("正在生成{}DaoServiceImpl...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath + "/service/impl", "DaoServiceImpl.ftl", dataModelMap, toPath, dataModelMap.get("className") + "DaoServiceImpl.java");
    }

    /**
     * 生成convert模块代码
     *
     * @param dataModelMap
     **/
    public void generateConvert(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/convert");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\convert\\" + dataModelMap.get("domainName");
        log.info("正在生成Convert模块......");
        // 生成转化器
        log.info("正在生成{}...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "Converter.ftl", dataModelMap, toPath, dataModelMap.get("className") + "Converter.java");
    }

    /**
     * 生成Business模块代码
     *
     * @param dataModelMap
     **/
    public void generateBusiness(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/business");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\business\\" + dataModelMap.get("domainName");
        log.info("正在生成Business模块......");
        // 生成BusinessService
        log.info("正在生成{}BusinessService...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath + "/service/interfaces", "BusinessService.ftl", dataModelMap, toPath, dataModelMap.get("className") + "BusinessService.java");
        // 生成BusinessServiceImpl
        log.info("正在生成{}BusinessServiceImpl...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath + "/service/impl", "BusinessServiceImpl.ftl", dataModelMap, toPath, dataModelMap.get("className") + "BusinessServiceImpl.java");
    }

    /**
     * 生成Controller模块代码
     *
     * @param dataModelMap
     **/
    public void generateController(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/controller");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\controller\\" + dataModelMap.get("domainName");
        log.info("正在生成Controller模块......");
        // 生成ServiceController
        log.info("正在生成{}ServiceController...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "ServiceController.ftl", dataModelMap, toPath, dataModelMap.get("className") + "ServiceController.java");
    }

    /**
     * 生成client模块代码
     *
     * @param dataModelMap
     **/
    public void generateClient(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        String tableName = (String) dataModelMap.get("tableName");
        tableName = tableName.replace("tb_", "");
        dataModelMap.put("restClassName", tableName.replace("_", "-"));
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/client");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\client\\" + dataModelMap.get("domainName");
        log.info("正在生成Client模块......");
        // 生成DTO
        log.info("正在生成{}DTO...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "DTO.ftl", dataModelMap, toPath, dataModelMap.get("className") + "DTO.java");
        // 生成ServiceConstants
        log.info("正在生成{}ServiceConstants...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "ServiceConstants.ftl", dataModelMap, toPath, dataModelMap.get("className") + "ServiceConstants.java");
        // 生成FeignClient
        log.info("正在生成{}FeignClient...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "FeignClient.ftl", dataModelMap, toPath, dataModelMap.get("className") + "FeignClient.java");
        // 生成FeignClientFallbackFactory
        log.info("正在生成{}FeignClientFallbackFactory...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "FeignClientFallbackFactory.ftl", dataModelMap, toPath, dataModelMap.get("className") + "FeignClientFallbackFactory.java");
    }

    /**
     * 生成app模块代码
     *
     * @param dataModelMap
     **/
    public void generateApp(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/app");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\app\\" + dataModelMap.get("domainName");
        log.info("正在生成App模块......");
        // 生成AddOrUpdateCmdExe
        log.info("正在生成{}AddOrUpdateCmdExe...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "AddOrUpdateCmdExe.ftl", dataModelMap, toPath + "\\executor", dataModelMap.get("className") + "AddOrUpdateCmdExe.java");
        // 生成GetByIdQryExe
        log.info("正在生成{}GetByIdQryExe...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "GetByIdQryExe.ftl", dataModelMap, toPath + "\\executor\\query", dataModelMap.get("className") + "GetByIdQryExe.java");
        // 生成FindAllQryExe
        log.info("正在生成{}FindAllQryExe...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "FindAllQryExe.ftl", dataModelMap, toPath + "\\executor\\query", dataModelMap.get("className") + "FindAllQryExe.java");
        // 生成PageQryExe
        log.info("正在生成{}ConditionPageQryExe...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "ConditionPageQryExe.ftl", dataModelMap, toPath + "\\executor\\query", dataModelMap.get("className") + "ConditionPageQryExe.java");
        // 生成DeleteByIdCmdExe
        log.info("正在生成{}DeleteByIdCmdExe...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "DeleteByIdCmdExe.ftl", dataModelMap, toPath + "\\executor", dataModelMap.get("className") + "DeleteByIdCmdExe.java");
        // 生成ServingServiceImpl
        log.info("正在生成{}ServingServiceImpl...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "ServingServiceImpl.ftl", dataModelMap, toPath, dataModelMap.get("className") + "ServingServiceImpl.java");
    }

    /**
     * 生成adapter模块代码
     *
     * @param dataModelMap
     **/
    public void generateAdapter(Map<String, Object> dataModelMap) throws IOException, TemplateException {
        // 根据数据表生成代码
        Resource resource = new ClassPathResource("templates/adapter");
        log.info("目录：{}", resource.getFile().getPath());
        String ftlPath = resource.getFile().getPath();

        String toPath = "D:\\app\\mycola\\adapter\\web\\" + dataModelMap.get("domainName");
        log.info("正在生成Adapter模块......");
        // 生成ServingController
        log.info("正在生成{}ServingController...", dataModelMap.get("className"));
        FreemarkerUtils.generateFile(ftlPath, "ServingController.ftl", dataModelMap, toPath, dataModelMap.get("className") + "ServingController.java");
    }
}
